home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / lib / sunbird / js / calWcapCachedCalendar.js < prev    next >
Encoding:
JavaScript  |  2007-05-23  |  19.0 KB  |  590 lines

  1. /* -*- Mode: javascript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public
  6.  * License Version 1.1 (the "License"); you may not use this file
  7.  * except in compliance with the License. You may obtain a copy of
  8.  * the License at http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS
  11.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  12.  * implied. See the License for the specific language governing
  13.  * rights and limitations under the License.
  14.  *
  15.  * The Original Code is mozilla.org code.
  16.  *
  17.  * The Initial Developer of the Original Code is Sun Microsystems, Inc.
  18.  * Portions created by Sun Microsystems are Copyright (C) 2006 Sun
  19.  * Microsystems, Inc. All Rights Reserved.
  20.  *
  21.  * Original Author: Daniel Boelzle (daniel.boelzle@sun.com)
  22.  *
  23.  * Contributor(s):
  24.  *
  25.  *
  26.  * Alternatively, the contents of this file may be used under the terms of
  27.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  28.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29.  * in which case the provisions of the GPL or the LGPL are applicable instead
  30.  * of those above. If you wish to allow use of your version of this file only
  31.  * under the terms of either the GPL or the LGPL, and not to allow others to
  32.  * use your version of this file under the terms of the NPL, indicate your
  33.  * decision by deleting the provisions above and replace them with the notice
  34.  * and other provisions required by the GPL or the LGPL. If you do not delete
  35.  * the provisions above, a recipient may use your version of this file under
  36.  * the terms of any one of the NPL, the GPL or the LGPL.
  37.  *
  38.  * ***** END LICENSE BLOCK ***** */
  39.  
  40. /**
  41.  * PROTOTYPE!
  42.  */
  43.  
  44. // globals:
  45. var g_localCals = {};
  46. var g_localCalsLastSync = {};
  47.  
  48. function calWcapCachedCalendar() {
  49.     this.wrappedJSObject = this;
  50.     this.m_observers = [];
  51.     this.m_observerMultiplexer = new ObserverMultiplexer(this);
  52.     this.m_syncQueue = new RequestQueue();
  53. }
  54. calWcapCachedCalendar.prototype = {
  55.     m_ifaces: [ Components.interfaces.calIWcapCalendar,
  56.                 Components.interfaces.calICalendar,
  57.                 Components.interfaces.calICalendarProvider,
  58.                 Components.interfaces.nsIInterfaceRequestor,
  59.                 Components.interfaces.nsIClassInfo,
  60.                 Components.interfaces.nsISupports ],
  61.     
  62.     // nsISupports:
  63.     QueryInterface:
  64.     function( iid )
  65.     {
  66.         for each ( var iface in this.m_ifaces ) {
  67.             if (iid.equals( iface ))
  68.                 return this;
  69.         }
  70.         throw Components.results.NS_ERROR_NO_INTERFACE;
  71.     },
  72.     
  73.     // nsIClassInfo:
  74.     getInterfaces:
  75.     function( count )
  76.     {
  77.         count.value = this.m_ifaces.length;
  78.         return this.m_ifaces;
  79.     },
  80.     get classDescription() {
  81.         return calWcapCalendarModule.WcapCalendarInfo.classDescription;
  82.     },
  83.     get contractID() {
  84.         return calWcapCalendarModule.WcapCalendarInfo.contractID;
  85.     },
  86.     get classID() {
  87.         return calWcapCalendarModule.WcapCalendarInfo.classID;
  88.     },
  89.     getHelperForLanguage: function( language ) { return null; },
  90.     implementationLanguage:
  91.     Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
  92.     flags: 0,
  93.     
  94.     // nsIInterfaceRequestor:
  95.     getInterface:
  96.     function( iid, instance )
  97.     {
  98.         return this.remoteCal.getInterface( iid, instance );
  99.     },
  100.     
  101.     m_observerMultiplexer: null,
  102.     m_remoteCal: null,
  103.     get remoteCal() {
  104.         return this.m_remoteCal;
  105.     },
  106.     set remoteCal(cal) {
  107.         if (this.m_remoteCal != null)
  108.             this.m_remoteCal.removeObserver( this.m_observerMultiplexer );
  109.         if (cal != null) {
  110.             cal.addObserver( this.m_observerMultiplexer );
  111.             cal.superCalendar = this;
  112.         }
  113.         return (this.m_remoteCal = cal);
  114.     },
  115.     
  116.     getCalKey:
  117.     function()
  118.     {
  119.         // xxx todo: better eoncoding?
  120.         return encodeURIComponent( this.uri.spec /* i.e. remote uri */ +
  121.                                    "?calid=" + this.calId );
  122.     },
  123.     
  124.     m_localCal: null,
  125.     get localCal() {
  126.         if (this.m_localCal == null) {
  127.             // xxx todo: sharing? storage cal_id: STRING?
  128.             // assure logged in, so userId/calId is properly set:
  129.             this.remoteCal.session.getSessionId();
  130.             // xxx todo: better eoncoding?
  131.             var key = this.getCalKey();
  132.             var cal = g_localCals[key];
  133.             if (!cal) {
  134.                 this.log( "creating cache calendar for calId " + this.calId );
  135.                 var uri;
  136.                 if (CACHE == "memory") { // in-memory caching
  137.                     uri = this.uri.clone();
  138.                     uri.path += ("?calid=" + this.calId);
  139.                 }
  140.                 else {
  141.                     // xxx todo: change storage cal to support STRING cal_id
  142.                     var dbPath = CACHE_DIR.clone();
  143.                     dbPath.append( key + ".sdb" );
  144.                     uri = getIoService().newFileURI( dbPath );
  145.                 }
  146.                 this.log( "local cal uri: " + uri.spec );
  147.                 try {
  148.                     cal = getCalendarManager().createCalendar( CACHE, uri );
  149.                     g_localCals[key] = cal;
  150.                 }
  151.                 catch (exc) {
  152.                     this.notifyError( exc );
  153.                 }
  154.             }
  155.             this.localCal = cal;
  156.         }
  157.         return this.m_localCal;
  158.     },
  159.     set localCal(cal) {
  160.         if (cal != null) {
  161.             cal.suppressAlarms = this.remoteCal.suppressAlarms; // sync setting
  162.             cal.superCalendar = this;
  163.         }
  164.         return (this.m_localCal = cal);
  165.     },
  166.     
  167.     toString:
  168.     function()
  169.     {
  170.         return "cached-wcap | " + this.remoteCal.toString();
  171.     },
  172.     log:
  173.     function( msg, context )
  174.     {
  175.         return this.remoteCal.log( msg, context ? context : this.toString() );
  176.     },
  177.     logError:
  178.     function( err, context )
  179.     {
  180.         return this.remoteCal.logError(
  181.             err, context ? context : this.toString() );
  182.     },
  183.     notifyError:
  184.     function( err )
  185.     {
  186.         debugger;
  187.         var msg = this.logError(err);
  188.         this.notifyObservers(
  189.             "onError",
  190.             err instanceof Components.interfaces.nsIException
  191.             ? [err.result, err.message] : [-1, msg] );
  192.     },
  193.     
  194.     // calICalendarProvider:
  195.     get prefChromeOverlay() {
  196.         return this.remoteCal.prefChromeOverlay;
  197.     },
  198.     // displayName attribute already part of calIWcapCalendar
  199.     createCalendar:
  200.     function( name, url, listener ) {
  201.         this.remoteCal.createCalendar( name, url, listener );
  202.     },
  203.     deleteCalendar: function( calendar, listener ) {
  204.         this.remoteCal.deleteCalendar( calendar, listener );
  205.     },
  206.     getCalendar: function( url ) {
  207.         return this.remoteCal.getCalendar( url );
  208.     },
  209.     
  210.     // calIWcapCalendar:
  211.     // xxx todo: generic facade helpers for most function delegates
  212.  
  213.     get session() { return this.remoteCal.session; },
  214.     get calId() { return this.remoteCal.calId; },
  215.     get calId_() { return this.remoteCal.calId_; },
  216.     set calId(id) {
  217.         this.localCal = null; // disconnect
  218.         return (this.remoteCal.calId = id);
  219.     },
  220.     get description() { return this.remoteCal.description; },
  221.     get displayName() { return this.remoteCal.displayName; },
  222.     get isOwnedCalendar() { return this.remoteCal.isOwnedCalendar; },
  223.     getCalendarProperties:
  224.     function( propName, out_count ) {
  225.         return this.remoteCal.getCalendarProperties(propName, out_count); },
  226.     getCalProps_:
  227.     function( bAsync ) {
  228.         return this.remoteCal.getCalProps_(bAsync);
  229.     },
  230.     
  231.     get defaultTimezone() {
  232.         return this.remoteCal.defaultTimezone;
  233.     },
  234.     
  235.     // calICalendar:
  236.     get name() {
  237.         return getCalendarManager().getCalendarPref( this, "NAME" );
  238.     },
  239.     set name(name) {
  240.         getCalendarManager().setCalendarPref( this, "NAME", name );
  241.         return name;
  242.     },
  243.     
  244.     get type() { return "wcap"; },
  245.     
  246.     m_bReadOnly: false,
  247.     get readOnly() { return (this.m_bReadOnly || this.remoteCal.readOnly); },
  248.     set readOnly(bReadOnly) { return (this.m_bReadOnly = bReadOnly); },
  249.     
  250.     get uri() { return this.remoteCal.uri; },
  251.     set uri(thatUri) {
  252.         this.localCal = null; // disconnect
  253.         return (this.remoteCal.uri = thatUri);
  254.     },
  255.     
  256.     m_superCalendar: null,
  257.     get superCalendar() { return this.m_superCalendar || this; },
  258.     set superCalendar(cal) { return (this.m_superCalendar = cal); },
  259.     
  260.     m_observers: null,
  261.     notifyObservers:
  262.     function( func, args )
  263.     {
  264.         this.m_observers.forEach(
  265.             function( obj ) {
  266.                 try {
  267.                     obj[func].apply( obj, args );
  268.                 }
  269.                 catch (exc) {
  270.                     // don't call notifyError() here:
  271.                     Components.utils.reportError( exc );
  272.                 }
  273.             } );
  274.     },
  275.     
  276.     addObserver:
  277.     function( observer )
  278.     {
  279.         if (this.m_observers.indexOf( observer ) == -1) {
  280.             this.m_observers.push( observer );
  281.         }
  282.     },
  283.     
  284.     removeObserver:
  285.     function( observer )
  286.     {
  287.         this.m_observers = this.m_observers.filter(
  288.             function(x) { return x != observer; } );
  289.     },
  290.     
  291.     // xxx todo: batch currently not used
  292.     startBatch: function() { this.notifyObservers( "onStartBatch", [] ); },
  293.     endBatch: function() { this.notifyObservers( "onEndBatch", [] ); },
  294.     
  295.     get suppressAlarms() { return this.remoteCal.suppressAlarms; },
  296.     set suppressAlarms( bSuppressAlarms ) {
  297.         this.remoteCal.suppressAlarms = bSuppressAlarms;
  298.         return (this.localCal.suppressAlarms = this.remoteCal.suppressAlarms);
  299.     },
  300.     
  301.     get canRefresh() { return true; },
  302.     
  303.     refresh:
  304.     function()
  305.     {
  306.         this.log( "refresh() call." );
  307.         if (this.remoteCal.canRefresh)
  308.             this.remoteCal.refresh();
  309.         if (this.localCal.canRefresh)
  310.             this.localCal.refresh();
  311.         // sync in changes to local calendar:
  312.         this.sync(null);
  313.         this.log( "refresh() returning." );
  314.     },
  315.     
  316.     adoptItem:
  317.     function( item, listener )
  318.     {
  319.         this.remoteCal.adoptItem( item, listener );
  320.     },
  321.     
  322.     addItem:
  323.     function( item, listener )
  324.     {
  325.         this.remoteCal.addItem( item, listener );
  326.     },
  327.     
  328.     modifyItem:
  329.     function( newItem, oldItem, listener )
  330.     {
  331.         this.remoteCal.modifyItem( newItem, oldItem, listener );
  332.     },
  333.     
  334.     deleteItem:
  335.     function( item, listener )
  336.     {
  337.         this.remoteCal.deleteItem( item, listener );
  338.     },
  339.     
  340.     getItem:
  341.     function( id, listener )
  342.     {
  343.         // xxx todo: testing
  344.         this.log( ">>>>>>>>>>>>>>>> getItem() call!");
  345.         this.refresh();
  346.         this.localCal.getItem( id, listener );
  347.         this.log( "getItem() returning." );
  348.     },
  349.     
  350.     getItems:
  351.     function( itemFilter, maxResults, rangeStart, rangeEnd, listener )
  352.     {
  353.         this.log( "getItems():\n\titemFilter=" + itemFilter +
  354.                   ",\n\tmaxResults=" + maxResults +
  355.                   ",\n\trangeStart=" + getIcalUTC(rangeStart) +
  356.                   ",\n\trangeEnd=" + getIcalUTC(rangeEnd) );
  357.         var this_ = this;
  358.         this.sync(
  359.             { // calIOperationListener:
  360.                 onOperationComplete:
  361.                 function( calendar, status, opType, id, detail )
  362.                 {
  363.                     if (listener != null) {
  364.                         if (status == NS_OK) {
  365.                             // delegate to local cal:
  366.                             this_.log("begin localCal.getItems().");
  367.                             this_.localCal.getItems(
  368.                                 itemFilter, maxResults, rangeStart, rangeEnd,
  369.                                 listener );
  370.                             this_.log("end localCal.getItems().");
  371.                         }
  372.                         else {
  373.                             listener.onOperationComplete(
  374.                                 calendar, status, opType, id, detail );
  375.                         }
  376.                     }
  377.                 },
  378.                 onGetResult:
  379.                 function( calendar, status, itemType, detail, count, items )
  380.                 {
  381.                     this_.notifyError( "unexpected onGetResult upon sync!" );
  382.                 }
  383.             } );
  384.     },
  385.     
  386.     syncChangesTo:
  387.     function( destCal, itemFilter, dtFrom, listener )
  388.     {
  389.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  390.     },
  391.     
  392.     getStampFile:
  393.     function()
  394.     {
  395.         var stampFile = this.localCal.uri.QueryInterface(
  396.             Components.interfaces.nsIFileURL ).file.clone();
  397.         stampFile.leafName += ".last_sync";
  398.         return stampFile;
  399.     },
  400.     
  401.     getStamp:
  402.     function()
  403.     {
  404.         var dtStamp = null;
  405.         if (CACHE == "memory") {
  406.             var stamp = g_localCalsLastSync[this.getCalKey()];
  407.             if (stamp) // return null if undefined
  408.                 dtStamp = stamp;
  409.         }
  410.         else {
  411.             var stampFile = this.getStampFile();
  412.             if (stampFile.exists()) {
  413.                 dtStamp = new CalDateTime();
  414.                 dtStamp.jsDate = new Date(stampFile.lastModifiedTime); // is UTC
  415.             }
  416.         }
  417.         return dtStamp;
  418.     },
  419.     
  420.     updateStamp:
  421.     function( dtStamp )
  422.     {
  423.         if (CACHE == "memory") {
  424.             g_localCalsLastSync[this.getCalKey()] = dtStamp.clone();
  425.         }
  426.         else {
  427.             var stampFile = this.getStampFile();
  428.             // xxx todo: setting lastModifiedTime does not work
  429.             //           (at least not on Windows)
  430. //         stampFile.lastModifiedTime = (dtStamp.nativeTime / 1000);
  431.             // xxx todo: changes inbetween get lost!
  432.             if (stampFile.exists())
  433.                 stampFile.remove(false);
  434.             stampFile.create(
  435.                 Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0700 );
  436.         }
  437.         if (LOG_LEVEL > 0) {
  438.             var st = dtStamp;
  439.             if (stampFile) {
  440.                 st = new CalDateTime();
  441.                 st.jsDate = new Date(stampFile.lastModifiedTime); // is UTC
  442.             }
  443.             this.log( "updated stamp to " + dtStamp + "\n\tnew stamp: " + st );
  444.         }
  445.     },
  446.     
  447.     m_syncQueue: null,
  448.     sync:
  449.     function( listener )
  450.     {
  451.         this.log( "sync(): queueing request." );
  452.         // serialize sync() calls into queue:
  453.         var this_ = this;
  454.         this.m_syncQueue.postRequest(
  455.             function( requestToken ) {
  456.                 this_.sync_req( requestToken, listener );
  457.             } );
  458.     },
  459.     sync_req:
  460.     function( requestToken, listener )
  461.     {
  462.         try {
  463.             var this_ = this;
  464.             var localCal = this.localCal;
  465.             var remoteCal = this.remoteCal;
  466.             
  467.             var dtFrom = this.getStamp();
  468.             const SYNC = Components.interfaces.calIWcapCalendar.SYNC;
  469.             
  470.             // first sync in changes from remote, then get items from locally:
  471.             remoteCal.syncChangesTo(
  472.                 localCal,
  473.                 Components.interfaces.calICalendar.ITEM_FILTER_ALL_ITEMS,
  474.                 dtFrom,
  475.                 { // calIOperationListener:
  476.                     onOperationComplete:
  477.                     function( calendar, status, opType, id, detail )
  478.                     {
  479.                         try {
  480.                             if (status == NS_OK) {
  481.                                 if (opType == SYNC) {
  482.                                     // write stamp: only if necessary
  483.                                     if (dtFrom == null ||
  484.                                         dtFrom.compare(detail) != 0) {
  485.                                         this_.updateStamp( detail );
  486.                                     }
  487.                                     if (listener != null) {
  488.                                         listener.onOperationComplete(
  489.                                             this_.superCalendar,
  490.                                             NS_OK, SYNC, null, null );
  491.                                     }
  492.                                 }
  493.                                 else {
  494.                                     throw new Components.Exception(
  495.                                         "unexpected operation type! " +
  496.                                         "(expected SYNC)" );
  497.                                 }
  498.                             }
  499.                             else {
  500.                                 if (listener != null) // forward errors:
  501.                                     listener.onOperationComplete(
  502.                                         calendar, status,
  503.                                         opType, id, detail );
  504.                                 // already notified in wcap cal:
  505. //                                 this_.notifyError( detail );
  506.                             }
  507.                         }
  508.                         catch (exc) {
  509.                             if (listener != null) {
  510.                                 listener.onOperationComplete(
  511.                                     this_.superCalendar,
  512.                                     Components.results.NS_ERROR_FAILURE,
  513.                                     SYNC, null, exc );
  514.                             }
  515.                             this_.notifyError( exc );
  516.                         }
  517.                         this_.m_syncQueue.requestCompleted( requestToken );
  518.                     },
  519.                     onGetResult:
  520.                     function( calendar, status, itemType, detail, count, items )
  521.                     {
  522.                         this_.notifyError( "unexpected onGetResult upon " +
  523.                                            "calling syncChangesTo()!" );
  524.                     }
  525.                 } );
  526.         }
  527.         catch (exc) {
  528.             if (listener != null) {
  529.                 listener.onOperationComplete(
  530.                     this.superCalendar, Components.results.NS_ERROR_FAILURE,
  531.                     SYNC, null, exc );
  532.             }
  533.             this.notifyError( exc );
  534.             this.m_syncQueue.requestCompleted( requestToken );
  535.         }
  536.         this.log( "sync_req() returning." );
  537.     }
  538. };
  539.  
  540. function ObserverMultiplexer( calendar ) {
  541.     this.wrappedJSObject = this;
  542.     this.m_calendar = calendar;
  543. }
  544. ObserverMultiplexer.prototype = {
  545.     m_calendar: null,
  546.     
  547.     // calIObserver:
  548.     onStartBatch:
  549.     function()
  550.     {
  551.         this.m_calendar.notifyObservers( "onStartBatch", [] );
  552.     },
  553.     onEndBatch:
  554.     function()
  555.     {
  556.         this.m_calendar.notifyObservers( "onEndBatch", [] );
  557.     },
  558.     onLoad:
  559.     function()
  560.     {
  561.         this.m_calendar.notifyObservers( "onLoad", [] );
  562.     },
  563.     onAddItem:
  564.     function( item )
  565.     {
  566.         this.m_calendar.notifyObservers( "onAddItem", [item] );
  567.     },
  568.     onModifyItem:
  569.     function( newItem, oldItem )
  570.     {
  571.         this.m_calendar.notifyObservers( "onModifyItem", [newItem, oldItem] );
  572.     },
  573.     onDeleteItem:
  574.     function( item )
  575.     {
  576.         this.m_calendar.notifyObservers( "onDeleteItem", [item] );
  577.     },
  578.     onAlarm:
  579.     function( item )
  580.     {
  581.         this.m_calendar.notifyObservers( "onAlarm", [item] );
  582.     },
  583.     onError:
  584.     function( errNo, msg )
  585.     {
  586.         this.m_calendar.notifyObservers( "onError", [errNo, msg] );
  587.     }
  588. };
  589.  
  590.